/*
 * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 * 
**/
#include "biodevices.h"
#include <QDBusInterface>
#include <QSettings>
#include <QFile>

#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>

#include "generic.h"


BioDevices::BioDevices(bool isIgnoreQrCode, QObject *parent)
    : QObject(parent),
      isShowHotPlug(false),
      useFirstDevice(false),
      m_isIgnoreQrCode(isIgnoreQrCode)
{
    connectToService();
    getDevicesList();

    useFirstDevice = getUseFirstDevice();
}

void BioDevices::connectToService()
{
    qRegisterMetaType<DeviceInfo>();
    qDBusRegisterMetaType<DeviceInfo>();

    serviceInterface = new QDBusInterface(BIO_DBUS_SERVICE,
                                          BIO_DBUS_PATH,
                                          BIO_DBUS_INTERFACE,
                                          QDBusConnection::systemBus());

    connect(serviceInterface, SIGNAL(USBDeviceHotPlug(int, int, int)),
            this, SLOT(onUSBDeviceHotPlug(int,int,int)));
    serviceInterface->setTimeout(2147483647);
}

void BioDevices::onUSBDeviceHotPlug(int deviceId, int action, int devNumNow)
{
    qDebug() << deviceId << action << devNumNow;
    QString  text = "";
    if(action == -1){
        DeviceInfoPtr device = findDevice(deviceId);
        if(device)
             text = tr("Unplugging of %1 device detected").arg(bioTypeToString_tr(device->biotype));
    }

    getDevicesList();

    emit deviceCountChanged(deviceInfos.size());

    if(action == 1){
        DeviceInfoPtr device = findDevice(deviceId);
        if(device)
             text = tr("%1 device insertion detected").arg(bioTypeToString_tr(device->biotype));
    }

    if(isShowHotPlug && text != ""){
        QDBusInterface iface("org.freedesktop.Notifications",
                             "/org/freedesktop/Notifications",
                             "org.freedesktop.Notifications",
                             QDBusConnection::sessionBus());
        QList<QVariant> args;
        args<<(tr("ukui-biometric-manager"))
           <<((unsigned int) 0)
          <<"biometric-manager"
         <<tr("biometric")
        <<text
        <<QStringList()
        <<QVariantMap()
        <<(int)-1;
        iface.callWithArgumentList(QDBus::AutoDetect,"Notify",args);
    }
}


/**
 * 获取设备列表
 */
void BioDevices::getDevicesList()
{
    /* 返回值为 i -- int 和 av -- array of variant */
    QDBusMessage msg = serviceInterface->call("GetDevList");
    if(msg.type() == QDBusMessage::ErrorMessage){
        LOG() << msg.errorMessage();
        return;
    }
    /* 设备数量 */
    int deviceNum = msg.arguments().at(0).toInt();
    bool bEnableBioAuth = GetBioAuthEnable();

    /* 读取设备详细信息，并存储到列表中 */
    QDBusArgument argument = msg.arguments().at(1).value<QDBusArgument>();
    QList<QVariant> infos;
    argument >> infos;

    deviceInfos.clear();

    for(int i = 0; i < deviceNum; i++) {
        DeviceInfoPtr pDeviceInfo = std::make_shared<DeviceInfo>();
        infos.at(i).value<QDBusArgument>() >> *pDeviceInfo;

        if(pDeviceInfo->device_available > 0) {  //设备可用
            if (pDeviceInfo->biotype == REMOTE_QRCODE_TYPE && m_isIgnoreQrCode)
                continue;
            if (!bEnableBioAuth && pDeviceInfo->biotype <= BIOTYPE_VOICEPRINT)
                continue;
            deviceInfos.push_back(pDeviceInfo);
        }            
    }
}


int BioDevices::count()
{
    return deviceInfos.size();
}

int BioDevices::GetUserDevCount(int uid)
{
    int count = 0;
    QDBusMessage msg = serviceInterface->call("GetDevList");
    if(msg.type() == QDBusMessage::ErrorMessage){
        LOG() << msg.errorMessage();
        return 0;
    }

    int deviceNum = msg.arguments().at(0).toInt();

    QDBusArgument argument = msg.arguments().at(1).value<QDBusArgument>();
    QList<QVariant> infos;
    argument >> infos;

    for(int i = 0; i < deviceNum; i++) {
        DeviceInfoPtr pDeviceInfo = std::make_shared<DeviceInfo>();
        infos.at(i).value<QDBusArgument>() >> *pDeviceInfo;
        if(pDeviceInfo->device_available > 0 && GetUserDevFeatureCount(uid,pDeviceInfo->device_id)>0)     //设备可用
            count++;
    }
    return count;

}

int BioDevices::GetUserDevFeatureCount(int uid,int drvid)
{
    // stop last option
    serviceInterface->call("StopOps", QVariant(drvid), QVariant(3000));
    QDBusMessage FeatureResult = serviceInterface->call(QStringLiteral("GetFeatureList"),drvid,uid,0,-1);
    if(FeatureResult.type() == QDBusMessage::ErrorMessage)
    {
            qWarning() << "GetFeatureList error:" << FeatureResult.errorMessage();
            return 0;
    }
    return FeatureResult.arguments().takeFirst().toInt();
}

int BioDevices::getFeatureCount(int uid, int indexStart, int indexEnd)
{
    int res = 0;
    for(int i = 0; i < deviceInfos.count(); i++) {
        DeviceInfoPtr deviceInfo = deviceInfos.at(i);
        QDBusReply<int> reply = serviceInterface->call("StopOps", QVariant(deviceInfo->device_id), QVariant(3000));
        QDBusMessage featurecount = serviceInterface->call("GetFeatureList",deviceInfo->device_id,uid,indexStart,indexEnd);
        if(featurecount.type() == QDBusMessage::ErrorMessage)
        {
                qWarning() << "GetFeatureList error:" << featurecount.errorMessage();
        }else{
            res += featurecount.arguments().takeFirst().toInt();
        }
    }
    return res;
}

QMap<int, QList<DeviceInfo>> BioDevices::getAllDevices()
{
    QMap<int, QList<DeviceInfo>> devices;

    for(auto deviceInfo : deviceInfos) {
        if (deviceInfo->biotype == REMOTE_QRCODE_TYPE)  // 终端不使用扫码
            continue;
        devices[deviceInfo->biotype].push_back(*deviceInfo);
    }

    return devices;
}

QMap<int, QList<DeviceInfo>> BioDevices::getUserDevices(int uid)
{
    QMap<int, QList<DeviceInfo>> devices;
    bool isQRCodeEnable = GetQRCodeEnable();
    for(auto deviceInfo : deviceInfos) {
        if(GetUserDevFeatureCount(uid,deviceInfo->device_id) > 0) {
            if (!isQRCodeEnable && deviceInfo->biotype == REMOTE_QRCODE_TYPE)    // 关闭了二维码功能
                continue;
            devices[deviceInfo->biotype].push_back(*deviceInfo);
        }
    }

    return devices;
}

QList<DeviceInfo> BioDevices::getDevices(int type)
{
    QList<DeviceInfo> devices;

    for(auto deviceInfo : deviceInfos) {
        if (deviceInfo->biotype == type)
            devices.push_back(*deviceInfo);
    }

    return devices;
}

void BioDevices::setIsShowHotPlug(bool isShow)
{
    isShowHotPlug = isShow;
}

bool BioDevices::getUseFirstDevice()
{
    QSettings settings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat);
    return settings.value("UseFirstDevice").toBool();
}

bool BioDevices::GetHiddenSwitchButton()
{
    QSettings sysSettings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat);
    if(sysSettings.contains("HiddenSwitchButton"))
        return sysSettings.value("HiddenSwitchButton").toBool();
    else
        return false;
}

int BioDevices::getFailedTimes()
{
    QSettings sysSettings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat);
    if(sysSettings.contains("MaxFailedTimes"))
        return sysSettings.value("MaxFailedTimes").toInt();
    else
        return 3;
}

bool BioDevices::GetQRCodeEnable()
{
    bool isEnable = false;
    QSettings sysSettings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat);
    sysSettings.beginGroup("Functions");
    if (sysSettings.allKeys().contains("EnableQRCode")) {
        isEnable = sysSettings.value("EnableQRCode").toBool();
    }
    sysSettings.endGroup();
    return isEnable;
}

bool BioDevices::GetBioAuthEnable()
{
    bool isEnable = false;
    QSettings sysSettings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat);
    if (sysSettings.allKeys().contains("EnableAuth")) {
        isEnable = sysSettings.value("EnableAuth").toBool();
    }
    return isEnable;
}

DeviceInfoPtr BioDevices::getDefaultDevice(uid_t uid)
{
    if(deviceInfos.size() <= 0)
        return nullptr;

    QString defaultDeviceName;

    struct passwd *pwd = getpwuid(uid);
    QString userConfigFile = QString(pwd->pw_dir) + "/.biometric_auth/ukui_biometric.conf";
    QSettings userConfig(userConfigFile, QSettings::IniFormat);
	qDebug() << userConfig.fileName();
    defaultDeviceName = userConfig.value(DEFAULT_DEVICE).toString();
	qDebug() << defaultDeviceName;

    if(defaultDeviceName.isEmpty() || !findDevice(defaultDeviceName)) {
        QSettings sysConfig(GET_STR(CONFIG_FILE), QSettings::IniFormat);
        defaultDeviceName = sysConfig.value(DEFAULT_DEVICE).toString();
    }

    if(defaultDeviceName.isEmpty() || !findDevice(defaultDeviceName)){
        QString userConfigFile = QString(pwd->pw_dir) + "/.biometric_auth/ukui_biometric.conf";
        QSettings userConfig(userConfigFile, QSettings::IniFormat);
        defaultDeviceName = userConfig.value(DEFAULT_DEVICE).toString();


    }
    qDebug() << "default device: " << defaultDeviceName;
    // 终端不默认使用第一个设备
    if(defaultDeviceName.isEmpty()){
//        if(!useFirstDevice)
//            return nullptr;
//        else
//            return getFirstDevice(uid);
        return nullptr;
    }
    bool defValid = false;
    DeviceInfoPtr ptrDevInfo = findDevice(defaultDeviceName);
    if (ptrDevInfo) {
        if (GetUserDevFeatureCount(uid,ptrDevInfo->device_id) > 0) {
            defValid = true;
        }
    }

    if (!defValid) {
//        if(!useFirstDevice)
//            ptrDevInfo = nullptr;
//        else
//            ptrDevInfo = getFirstDevice(uid);
        ptrDevInfo = nullptr;
    }

    return ptrDevInfo;
}

int BioDevices::GetLastDevice(const QString &userName)
{
    int nLastDevId = -1;
    QSettings sysSettings(QString(SHARE_BIOMETRIC_CONFIG_PATH).arg(userName), QSettings::IniFormat);
    sysSettings.beginGroup("Common");
    if (sysSettings.allKeys().contains("LastDeviceId")) {
        nLastDevId = sysSettings.value("LastDeviceId").toInt();
    }
    sysSettings.endGroup();
    return nLastDevId;
}

void BioDevices::SetLastDevice(const QString &userName, int drvid)
{
    if (drvid < 0) {
        return;
    }
    QString desConfPath = QString(SHARE_BIOMETRIC_CONFIG_PATH).arg(userName);
    QFile fileConf(desConfPath);
    if (fileConf.exists()) {
        QSettings sysSettings(desConfPath, QSettings::IniFormat);
        sysSettings.beginGroup("Common");
        sysSettings.setValue("LastDeviceId", drvid);
        sysSettings.endGroup();
    } else {
        QSettings sysSettings(desConfPath, QSettings::IniFormat);
        sysSettings.beginGroup("Common");
        sysSettings.setValue("LastDeviceId", drvid);
        sysSettings.endGroup();
        sysSettings.sync();
        QFile file(desConfPath);
        file.setPermissions(QFile::WriteUser | QFile::ReadUser | QFile::WriteOther | QFile::ReadOther);
    }
}

DeviceInfoPtr BioDevices::findDevice(const int id)
{
    for(auto deviceInfo : deviceInfos) {
        if(deviceInfo->device_id == id)
            return deviceInfo;
    }
    //qDebug() << deviceName << "doesn't exists";
    return nullptr;
}

DeviceInfoPtr BioDevices::findDevice(const QString &deviceName)
{
    for(auto deviceInfo : deviceInfos) {
        if(deviceInfo->device_shortname == deviceName)
            return deviceInfo;
    }
    qDebug() << deviceName << "doesn't exists";
    return nullptr;
}

DeviceInfoPtr BioDevices::getFirstDevice(int uid)
{
    for (DeviceInfoPtr devInfo : deviceInfos) {
        if (devInfo && GetUserDevFeatureCount(uid, devInfo->device_id) > 0) {
            return devInfo;
        }
    }

    return nullptr;
}

QString BioDevices::bioTypeToString_tr(int type)
{
    switch(type) {
    case BIOTYPE_FINGERPRINT:
        return tr("FingerPrint");
    case BIOTYPE_FINGERVEIN:
        return tr("FingerVein");
    case BIOTYPE_IRIS:
        return tr("Iris");
    case BIOTYPE_FACE:
        return tr("Face");
    case BIOTYPE_VOICEPRINT:
        return tr("VoicePrint");
    case REMOTE_QRCODE_TYPE:
        return tr("QRCode");
    }
    return QString();
}

DeviceList BioDevices::GetDevList()
{
    return deviceInfos;
}

StatusReslut BioDevices::UpdateStatus(int drvid)
{
    StatusReslut status;
    QDBusMessage result = serviceInterface->call(QStringLiteral("UpdateStatus"), drvid);
    if(result.type() == QDBusMessage::ErrorMessage)
    {
        qWarning() << "UpdateStatus error:" << result.errorMessage();
        status.result = -1;
        return status;
    }

    status.result           = result.arguments().at(0).toInt();
    status.enable           = result.arguments().at(1).toInt();
    status.devNum           = result.arguments().at(2).toInt();
    status.devStatus        = result.arguments().at(3).toInt();
    status.opsStatus        = result.arguments().at(4).toInt();
    status.notifyMessageId  = result.arguments().at(5).toInt();

    return status;
}
